//
//  OperationQueueTableViewController.h
//  OperationQueueDemo
//
//  Created by 苏沫离 on 2018/8/10.
//  Copyright © 2018年 苏沫离. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface OperationQueueTableViewController : UITableViewController

@end

/*
 [NSOperationQueue](https://developer.apple.com/documentation/foundation/nsoperationqueue?changes=latest_minor&language=objc)：
 
管理操作执行的队列。
 
操作队列根据其优先级和就绪程度执行其排队的NSOperation对象。在添加到操作队列后，操作将保持在其队列中，直到它报告完成其任务为止。在队列被添加后，不能直接从队列中删除操作。
操作队列保留操作直到完成，队列本身保留到所有操作完成。使用未完成的操作挂起操作队列可能导致内存泄漏。
 
 
 1、确定执行顺序
 
队列中的操作是根据它们的准备、优先级级别和互操作依赖关系来组织的，并相应地执行。如果所有排队的操作都具有相同的queuePriority并准备好在放入队列时执行(也就是说，它们的就绪属性返回yes)，那么它们将按照提交到队列的顺序执行。否则，操作队列总是执行优先级最高的操作。
 
 但是，您永远不应该依赖队列语义来确保操作的特定执行顺序，因为操作准备状态的更改可能会更改最终的执行顺序。操作间依赖关系为操作提供了绝对的执行顺序，即使这些操作位于不同的操作队列中。一个操作对象直到它的所有依赖操作都完成执行后才被认为准备好执行。
 
 
 取消操作
 
 完成任务并不一定意味着操作完成了任务;一个操作也可以被取消。取消操作对象会将对象留在队列中，但会通知对象应该尽快停止其任务。对于当前正在执行的操作，这意味着操作对象的工作代码必须检查取消状态，停止它正在执行的操作，并将自己标记为已完成。对于排队但尚未执行的操作，队列必须仍然调用操作对象的start方法，以便它能够处理取消事件并将自己标记为已完成。
 
 
 取消操作会导致操作忽略它可能具有的任何依赖项。这种行为使队列能够尽快执行操作的start方法。开始方法依次将操作移动到完成状态，以便可以将其从队列中删除。
 
 KVO兼容的特性
 
 NSOperationQueue类是符合键值编码(KVC)和键值观察(KVO)的。您可以根据需要观察这些属性，以控制应用程序的其他部分。要观察属性，使用以下关键路径:
 虽然可以将观察者附加到这些属性，但是不应该使用Cocoa绑定将它们绑定到应用程序用户界面的元素。与用户界面关联的代码通常只能在应用程序的主线程中执行。然而，与操作队列相关联的KVO通知可能发生在任何线程中。
 
 线程安全
 
从多个线程中使用一个NSOperationQueue对象是安全的，无需创建额外的锁来同步对该对象的访问。
 
操作队列使用调度框架来启动其操作的执行。因此，操作总是在单独的线程上执行，而不管它们是被指定为同步的还是异步的。
 
 
 
####2.1、```NSOperationQueue```的属性与方法

#####2.1.1、访问特定操作队列
 
属性|值描述
-|-
```mainQueue```|一个只读属性的```NSOperationQueue```，返回绑定到主线程的默认操作队列；返回的队列每次在应用程序的主线程上执行一个操作。（```class```修饰，由类对象调用）
```currentQueue```|一个只读属性的```NSOperationQueue```，在运行的操作对象中使用此方法来获取当前操作的操作队列。从运行操作的上下文外部调用此方法通常会返回nil。（```class```修饰，由类对象调用）


#####2.1.2、管理队列中的操作
 
 
方法|方法描述
-|-
```- (void)addOperation:(NSOperation *)op```|将指定的操作添加到操作队列。添加后，指定的操作将保留在操作队列中，直到执行完毕。一个操作对象一次最多可以在一个操作队列中，如果操作已经在另一个操作队列中，该方法将抛出一个```NSInvalidArgumentException```异常。类似地，如果操作正在执行或已经执行完毕，该方法将抛出```NSInvalidArgumentException```异常。
```- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait```|将指定的操作添加到操作队列中；添加后，指定的操作将保留在队列中，直到完成的方法返回YES为止；指定的操作完成后，系统会将这些操作集从```operations```数组中移除。参数```ops```要添加到操作队列中的操作集；参数```wait```如果```YES```，则阻塞当前线程，直到所有指定的操作执行完毕；如果为```NO```，则不会堵塞当前线程，立即返回。
```- (void)addOperationWithBlock:(void (^)(void))block```|在操作中包装指定的块并将其添加到操作队列。参数```block```是从操作中执行的块，该块不接受任何参数，也没有返回值。
```- (void)waitUntilAllOperationsAreFinished```|阻塞当前线程，直到队列中所有操作执行完成为止。在此期间，当前线程不能向队列添加操作，但其他线程可以；完成所有挂起操作后，该方法返回；如果队列中没有操作，此方法立即返回；完成返回后，系统会将```operations```的所有操作移除。
```- (void)cancelAllOperations```|取消所有排队和执行的操作。此方法会对当前操作队列中的所有操作调用```-(void)cancel```方法。取消操作不会自动将它们从队列中删除或停止当前正在执行的操作。对于排队和等待执行的操作，队列必须仍然尝试执行操作，然后才会识别它被取消并将其移动到完成状态。对于已经执行的操作，操作对象本身必须检查是否取消，并停止正在执行的操作，以便能够移动到完成状态。在这两种情况下，已完成(或已取消)的操作在从队列中删除之前仍然有机会执行其完成块。

 
属性|属性描述
-|-
```operations```|一个只读属性的数组，是当前操作队列中所有操作```NSOperation```集合。该数组包含多个```NSOperation```对象，其顺序与它们被添加到队列的顺序相同；这个顺序不一定反映执行这些操作的顺序。数组可能包含正在执行或等待执行的操作。列表还可能包含在数组最初被检索时正在执行的操作，但随后已经完成。
```operationCount```|一个只读属性的```NSUInteger```类型数据，表示操作队列中当前的并发数；


 

 
 
#####2.1.3、管理与配置操作队列
 
属性|属性描述
-|-
```qualityOfService```|应用于添加到操作队列的操作对象的服务级别```NSQualityOfService```。服务级别影响操作对象访问系统资源(如CPU时间、网络资源、磁盘资源等)的优先级；服务质量较高的操作优先于系统资源，因此可以更快地执行任务。对于自己创建的队列，默认值是```NSOperationQualityOfServiceBackground```。对于主线程方法返回的队列，默认值是```NSOperationQualityOfServiceUserInteractive```，无法更改。
```maxConcurrentOperationCount```|操作队列可以同时执行的最大数量；设置并发操作的数量不会影响当前正在执行的任何操作。可以使用苹果推荐值```NSOperationQueueDefaultMaxConcurrentOperationCount```，该值是根据当前系统条件动态确定的最大操作数。
```name```|操作队列的名称，使用此名称方便调试或分析代码。此属性的默认值是一个包含操作队列内存地址的字符串。
```underlyingQueue```|用于执行操作的分派队列```dispatch_queue_t```，默认值为nil。可以将此属性的值设置为现有的调度队列，以便将排队操作与提交到该调度队列的块穿插在一起；只有在队列中没有操作时才应该设置此属性的值，```当operationCount```不等于0时设置此属性的值会引发```NSInvalidArgumentException```。该分派队列不能是主队列```dispatch_get_main_queue```。该分派队列的服务质量级别集覆盖操作队列的服务质量；如果```OS_OBJECT_IS_OBJC```是YES，此属性将自动保留其分配的队列。

 

 
#####2.1.4、操作队列暂停执行
 
属性```suspended```：一个可读可写的布尔值，指示队列是否在主动调度要执行的操作。该属性的默认值为NO。当此属性的值为NO时，操作队列会启动队列中准备执行的操作；将此属性设置为YES可挂起操作队列中的任何排队操作，但已经执行的操作将继续执行。挂起的操作队列可以继续添加操作，但在将此属性更改为NO之前，这些操作不会计划执行。
 
属性```suspended```在操作队列的作用类似于GCD中的```dispatch_suspend()```函数，并不会立即暂停分派队列```dispatch_queue```中正在执行的任务，而是在当前任务执行完成后，暂停后续的任务执行。
 
 
 
 
 
 
 ###4、操作队列```NSOperationQueue```与分派队列```dispatch_queue_t```
 
 我们在开篇时提出：[NSOperation](https://developer.apple.com/documentation/foundation/nsoperation?changes=latest_minor&language=objc)与 [NSOperationQueue](https://developer.apple.com/documentation/foundation/nsoperationqueue?changes=latest_minor&language=objc) 是苹果提供的一套面向对象的基于GCD封装的多线程解决方案。那么苹果为什么要封装```GCD```？使用```NSOperationQueue```的优势是什么？
 

####4.1、```NSOperation```与```dispatch_block_t```

通过上文的学习，我们已经感受到```NSOperation```很像GCD中的```dispatch_block_t```。那么相比于```dispatch_block_t```，使用Operation的优势如下:
 
* 可以给代码块添加```completionBlock```, 在任务完成以后自己调用. 相对于GCD代码更简洁；类似于GCD的```dispatch_block_wait()```或者```dispatch_block_notify()```
* ```NSOperation```之间可以添加依赖关系，```- addDependency:```;
* 设置```NSOperation```的优先级；类似```dispatch_block_t```的qos
* 方便的设置operation取消操作；类似```dispatch_block_cancel()```
* 使用KVO观察对```NSOperation```状态的监听: ```isExcuting```, ```isFinished```, ```isCancelled```
 

####4.2、```NSOperationQueue```与```dispatch_queue_t```

相比于分派队列```dispatch_queue_t```，使用操作队列NSOperationQueue的优势如下:

* 对操作队列当前并发数的监控```operationCount```；
* 对操作队列最大并发数的设置```maxConcurrentOperationCount```；
* 可以调用```- cancelAllOperations```方法取消操作队列中尚未执行的操作；GCD中并没有提供取消分派队列```dispatch_queue_t```中任务的函数；
* 有时候我们很希望知道当前执行的操作队列是谁，我们可以使用```currentQueue```获取到当前的操作队列。但是GCD中分派队列```dispatch_queue_t```是按照层级结构来组织的，无法单用某个队列对象来描述“当前队列”，```dispatch_get_current_queue()```函数可能返回与预期不一致的结果，而且误用```dispatch_get_current_queue()```可能导致死锁，所以GCD中```dispatch_get_current_queue()```在 iOS 6已被废弃。


*
 */
